package com.ydl.learning.scala

/**
  * 型变
  * 官方文档:https://docs.scala-lang.org/zh-cn/tour/variances.html
  * scala中的型变分协变和逆变
  * 协变[+A]:向下转型
  * 逆变[-A]:向上转型
  *
  * @author ydl
  * @since 2021/3/15
  */
class Variances {

  abstract class Animal {
    def name: String
  }

  //类型 Cat 和 Dog 都是 Animal 的子类型。
  // Scala 标准库有一个通用的不可变的类 sealed abstract class List[+A]，其中类型参数 A 是协变的。
  // 这意味着 List[Cat] 是 List[Animal]，List[Dog] 也是 List[Animal]。
  // 直观地说，猫的列表和狗的列表都是动物的列表是合理的，你应该能够用它们中的任何一个替换 List[Animal]。
  case class Cat(name: String) extends Animal

  case class Dog(name: String) extends Animal

  /**
    * 协变
    * 方法 printAnimalNames 将接受动物列表作为参数，并且逐行打印出它们的名称。
    * 如果 List[A] 不是协变的，最后两个方法调用将不能编译，这将严重限制 printAnimalNames 方法的适用性。
    */
  def printAnimalNames(animals: List[Animal]): Unit = {
    animals.foreach { animal =>
      println(animal.name)
    }
  }

  val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom"))
  val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex"))

  printAnimalNames(cats)
  // Whiskers
  // Tom

  printAnimalNames(dogs)
  // Fido
  // Rex


  /**
    * 逆变
    * @tparam A
    */
  abstract class Printer[-A] {
    def print(value: A): Unit
  }

  class AnimalPrinter extends Printer[Animal] {
    def print(animal: Animal): Unit =
      println("The animal's name is: " + animal.name)
  }

  class CatPrinter extends Printer[Cat] {
    def print(cat: Cat): Unit =
      println("The cat's name is: " + cat.name)
  }

  val myCat: Cat = Cat("Boots")

  def printMyCat(printer: Printer[Cat]): Unit = {
    printer.print(myCat)
  }

  val catPrinter: Printer[Cat] = new CatPrinter
  val animalPrinter: Printer[Animal] = new AnimalPrinter

  printMyCat(catPrinter)
  printMyCat(animalPrinter)
}
